import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'intercept/base_intercept.dart';
import '../index.dart';

///http请求
class HttpManager {
  static const CONTENT_TYPE_JSON = "application/json";
  static const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";

  // ignore: non_constant_identifier_names
  int REQ_TIME_OUT;

  // ignore: non_constant_identifier_names
  int RESP_TIME_OUT;

  static Map<String, CancelToken> _cancelTokens =
      new Map<String, CancelToken>();

  Dio _jsonDio;

  Dio _dio;

  String mBaseUrl;

  // ignore: non_constant_identifier_names
  String CODE_NAME;

  // ignore: non_constant_identifier_names
  String MSG_NAME;

  // ignore: non_constant_identifier_names
  int CODE_SUCCESS_VALUE;

  HttpManager(this.mBaseUrl,
      // ignore: non_constant_identifier_names
      {this.CODE_NAME = "errcode",
      // ignore: non_constant_identifier_names
      this.CODE_SUCCESS_VALUE = 200,
      // ignore: non_constant_identifier_names
      this.MSG_NAME = "msg",
      // ignore: non_constant_identifier_names
      this.REQ_TIME_OUT = 50000,
      // ignore: non_constant_identifier_names
      this.RESP_TIME_OUT = 50000});

  ///get请求
  Stream<T> get<T>(
    String url,
    T buildResBean(resMap), {
    Map<String, dynamic> queryParams,
    dynamic dealIsSuccess(resMap, errCode, errMsg),
    BaseIntercept baseIntercept,
    List<Interceptor> interceptors,
    String baseUrl,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: true,
        queryParams: queryParams,
        dealIsSuccess: dealIsSuccess,
        baseIntercept: baseIntercept,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  ///post请求
  Stream<T> post<T>(
    String url,
    T buildResBean(resMap), {
    Map<String, dynamic> queryParams,
    dynamic dealIsSuccess(resMap, errCode, errMsg),
    BaseIntercept baseIntercept,
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: false,
        queryParams: FormData.fromMap(queryParams),
        dealIsSuccess: dealIsSuccess,
        baseIntercept: baseIntercept,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  ///json请求
  Stream<T> postJson<T>(
    String url,
    T buildResBean(resMap), {
    String jsonData,
    dynamic dealIsSuccess(resMap, errCode, errMsg),
    BaseIntercept baseIntercept,
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) {
    return _requestHttp<T>(url, buildResBean,
        isGet: false,
        isJson: true,
        jsonData: jsonData,
        dealIsSuccess: dealIsSuccess,
        baseIntercept: baseIntercept,
        interceptors: interceptors,
        isCancelable: isCancelable);
  }

  /// 参数说明  url 请求路径
  /// queryParams  是 请求参数
  /// baseWidget 和 baseInnerWidgetState 用于 加载 loading 和 设置 取消 CancelToken
  /// isCancelable 是设置改请求是否 能被取消 ， 必须建立在 传入baseWidget或者baseInnerWidgetState的基础之上
  /// isShowLoading 设置是否能显示 加载loading , 同样要建立在传入 baseWidget或者 baseInnerWidgetState 基础之上
  /// isShowErrorToast  这个是 设置请求失败后 是否要 吐司的
  Stream<T> _requestHttp<T>(
    String url,
    T buildResBean(resMap), {
    bool isGet,
    bool isJson = false,
    String jsonData,
    queryParams,
    dynamic dealIsSuccess(resMap, errCode, errMsg),
    BaseIntercept baseIntercept,
    List<Interceptor> interceptors,
    bool isCancelable = true,
  }) {
    Future future;
    StreamController<T> streamController = StreamController<T>();
    CancelToken cancelToken;
    _setInterceptOrCancelable(baseIntercept, isCancelable, cancelToken);

    _dio ??= initDio(mBaseUrl, interceptors: interceptors);

    if (isJson) {
      _jsonDio ??= initJsonDio(mBaseUrl, interceptors: interceptors);
      future = _jsonDio.post(url, data: jsonData, cancelToken: cancelToken);
    } else if (isGet) {
      future =
          _dio.get(url, queryParameters: queryParams, cancelToken: cancelToken);
    } else {
      future = _dio.post(url, data: queryParams, cancelToken: cancelToken);
    }

    future.then((data) {
      Utils.logLong("net succ:${mBaseUrl + url}");
      Utils.logLong("net succ:${data.toString()}");

      //这里要做错误处理
      //需要先过滤 error ， 根据和后台的 约定 ， 搞清楚什么是失败
      // 什么是成功  ， 这里只是根据了干货集中营的 返回 模拟了一下
      String res = data.toString();
      dynamic resMap = json.decode(res);
      int codeName = int.parse(resMap[CODE_NAME].toString());
      int errCode = codeName ?? -1;
      String errMsg = resMap[MSG_NAME] ??= "";

      dynamic realDealIsSuccess = dealIsSuccess ?? dealIsSuccessDefault;

      bool isSuccess = realDealIsSuccess(resMap, errCode, errMsg);

      if (!isSuccess) {
        Utils.logLong("net err by api $errCode $CODE_SUCCESS_VALUE");
        callError(
          streamController,
          NetError(code: errCode, msg: errMsg ?? "服务器响应错误", resMap: resMap),
          baseIntercept,
        );
      } else {
        Utils.logLong("net succ by api");
        streamController.add(buildResBean(resMap));
        streamController.close();
        cancelLoading(baseIntercept);
      }
    }).catchError((err) {
      Utils.logLong("net err:${err.toString()}");

      callError(
          streamController,
          NetError(
              code: -1,
              msg: (err is DioError)
                  ? formatErrorMessage(err.type)
                  : err.toString()),
          baseIntercept);
    });

    return streamController.stream;
  }

  static String formatErrorMessage(DioErrorType type) {
    switch (type) {
      case DioErrorType.DEFAULT:
        return "无法连接到服务器，请检查您的网络设置！";
      case DioErrorType.CONNECT_TIMEOUT:
        return "连接超时，请稍后再试！";
      case DioErrorType.RECEIVE_TIMEOUT:
        return "接收超时，请稍后再试！";
      case DioErrorType.RESPONSE:
        return "服务器睡着了，请稍后再试！";
      case DioErrorType.CANCEL:
        return "取消网络请求";
      default:
        return "未知网络错误";
    }
  }

  bool dealIsSuccessDefault(resMap, errCode, errMsg) {
    return int.parse(errCode.toString()) == CODE_SUCCESS_VALUE;
  }

  ///请求错误以后 做的一些事情
  void callError(StreamController streamController, NetError error,
      BaseIntercept baseIntercept) {
    if (baseIntercept != null) {
      baseIntercept.requestFailure(error.msg);
    }
    streamController.addError(error);
    streamController.close();
    cancelLoading(baseIntercept);
  }

  ///取消请求
  static void cancelHttp(String tag) {
    if (_cancelTokens.containsKey(tag)) {
      if (!_cancelTokens[tag].isCancelled) {
        _cancelTokens[tag].cancel();
      }
      _cancelTokens.remove(tag);
    }
  }

  ///配置dio
  Dio initJsonDio(baseUrl, {List<Interceptor> interceptors}) {
    return initDio(baseUrl,
        contentType: CONTENT_TYPE_JSON, interceptors: interceptors);
  }

  ///配置dio
  Dio initDio(baseUrl,
      {String contentType = CONTENT_TYPE_FORM,
      List<Interceptor> interceptors})  {
    Dio _dio = Dio();
    // 配置dio实例
    _dio.options.baseUrl = baseUrl;
    _dio.options.connectTimeout = REQ_TIME_OUT;
    _dio.options.receiveTimeout = RESP_TIME_OUT;
    _dio.options.contentType = contentType;

    //代理设置
    if (BuildConfig.isDebug) {
      //此处可以增加配置项，留一个设置代理的用户给测试人员配置，然后动态读取

      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        //config the http client
        client.findProxy = (uri) {
          // proxy all request to localhost:8888
          return HttpClient.findProxyFromEnvironment(uri, environment: {
//            "http_proxy": 'http://192.168.0.248:8888',
          });
        };
        //you can also create a new HttpClient to dio
        //return new HttpClient();
      };
    }

    //代理设置
    //此处可以增加配置项，留一个设置代理的用户给测试人员配置，然后动态读取
//    (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
//        (client) {
//      client.findProxy = (uri) {
//        return HttpClient.findProxyFromEnvironment(uri, environment: {
//          "http_proxy": 'http://192.168.0.248:8888',
//        });
//      };
//    };

    //证书配置
//    String PEM="XXXXX"; // certificate content
//    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate  = (client) {
//      client.badCertificateCallback=(X509Certificate cert, String host, int port){
//        if(cert.pem==PEM){ // Verify the certificate
//          return true;
//        }
//        return false;
//      };
//    };

    ///cookie 管理
    var cookieJar = CookieJar();
    _dio.interceptors.add(CookieManager(cookieJar));

    /// 添加拦截器
    interceptors ??= List<Interceptor>()..add(MyIntercept());
    interceptors.forEach((ic) {
      _dio.interceptors.add(ic);
    });

    return _dio;
  }

  ///测试是否真的 可以清除 的方法
  static void listCancelTokens() {
    _cancelTokens.forEach((key, value) {
      print("$key-----cancel---");
    });
  }

  ///配置是否能被取消
  void _setInterceptOrCancelable(
      BaseIntercept baseIntercept, bool isCancelable, CancelToken cancelToken) {
    if (baseIntercept != null) {
      baseIntercept.beforeRequest();
      //为了 能够取消 请求
      if (isCancelable) {
        cancelToken = _cancelTokens[baseIntercept.getInstanceName()] == null
            ? CancelToken()
            : _cancelTokens[baseIntercept.getInstanceName()];
        _cancelTokens[baseIntercept.getInstanceName()] = cancelToken;
      }
    }
  }

  void cancelLoading(BaseIntercept baseIntercept) {
    if (baseIntercept != null) {
      baseIntercept.afterRequest();
    }
  }
}

class NetError {
  int code;
  String msg;
  dynamic resMap;

  NetError({this.code, this.msg = '', this.resMap});
}

enum NetRespState {
  SUCCESS,
  ERROR,
  NO_DATA,
}
